# Python 趣味编程：第 9 章　
# 练习问题 9.2  3x3的控制器
# --------------------------
# 程序名: ex09-mine-set.py

from tkinter import Tk, Canvas, CENTER
from dataclasses import dataclass, field
import math

# 初始设定值
OFFSET_X = 100
OFFSET_Y = 100
CELL_SIZE = 40
FONT_SIZE = 20
CELL_CENTER = CELL_SIZE / 2
BOARD_WIDTH = 3
BOARD_HEIGHT = 3
FONT = "Helvetica " + str(FONT_SIZE)

# =================================================

# 管理整个游戏的棋局
@dataclass
class Board:
    width: int
    height: int
    # 地雷地点:准备好空集合
    mine: set=field(default_factory=set)
    # 开放的格子:空的集合　
    is_open: set=field(default_factory=set)

    # 方格索引是否有效的判断
    def is_valid(self, i, j):
        return 0 <= i < self.width and 0 <= j < self.height

    # 打开方格
    def open(self, i, j):
        if self.is_valid(i, j):     # 如果方格索引有效的话
            loc = (i, j)            # 生成元组
            self.is_open.add(loc)   # 添加到“开放方格”的集合

    # 创建表示周围方格的元组列表
    def neighbors(self, i, j):
        x = [(i-1, j-1), (i, j-1), (i+1, j-1),
             (i-1, j  ),           (i+1, j  ),
             (i-1, j+1), (i, j+1), (i+1, j+1)]
        return x

    # 数周围地雷的数量
    def count(self, i, j):
        c = 0
        for x in self.neighbors(i, j):   # 从周围的方格列表
            if x in self.mine:            # 找到的索引元组
                 c = c + 1                # 包含在地雷集合里的话，加算
        return c

# 画板的绘制
def draw_board(board):
    canvas.delete("all")                  # 暂时清除
    for i in range(board.width):          # i是宽度方向的下标
        for j in range(board.height):     # j是高度方向的下标
            text = ""
            if (i, j) in board.is_open:   # 方格是开着的
                if (i, j) in board.mine:  # 如果是地雷
                    text = "*"            # "*"
                else:                     # 如果不是地雷的话
                    text = str(board.count(i,j))  # 计数数字表示
            else:                         # 如果方格没有打开
                text = "-"                # 显示为"-"
            draw_text(i, j, text)     # 文本的显示

# 方格的显示(框和文字)
def draw_text(i, j, text):
    x = OFFSET_X + i * CELL_SIZE      # 从索引i计算x坐标
    y = OFFSET_Y + j * CELL_SIZE      # 从索引j计算y坐标
    canvas.create_rectangle(x, y, x + CELL_SIZE, y + CELL_SIZE) # 框架
    canvas.create_text(x + CELL_CENTER, y + CELL_CENTER,
                       text=text, font=FONT, anchor=CENTER)

# 点击时的处理
def on_click(event):
    x, y = (event.x, event.y)
    i = math.floor((x - OFFSET_X) / CELL_SIZE) # 从x计算索引i
    j = math.floor((y - OFFSET_Y) / CELL_SIZE) # 从y计算索引i
    print(i, j)  # 在调试时确认计算正确的方法的例子
    if board.is_valid(i, j):  # 有效的索引
        board.open(i, j)      # 打开方格
        draw_board(board)     # 重新绘制

# --------------------------
tk=Tk()
tk.title("Mine Sweeper")
canvas = Canvas(tk, width=500, height=400, bd=0)
canvas.pack()

canvas.bind('<Button-1>', on_click)  # 注册活动程序

# ----------------------------------
# 主程序
board = Board(BOARD_WIDTH, BOARD_HEIGHT)
board.mine.add((1, 1))
board.mine.add((2, 2))
draw_board(board)
